#include "stdafx.h"
#include "CommonNetwork.h"
#include "..\Common\CommonUtil.h"
#include "..\Common\CommonWinsock.h"

network::network(void)
: m_sock(NULL), m_addrlen(0), m_NetworkType(NC_NONE), m_Event(NULL), m_ResultEventProcess(0) {
	InitializeCriticalSection(&m_SendCriticalSection);
	InitializeCriticalSection(&m_RecvCriticalSection);	
}

network::~network()
{
	DeleteCriticalSection(&m_SendCriticalSection);
	DeleteCriticalSection(&m_RecvCriticalSection);

	Close();
}

bool network::Initialize(NETWORK_CODE networkcode, string ip, int port)
{
	switch(networkcode)
	{
	case NC_TCP_LISTENING:
		return InitializeListening(ip, port);
	
	case NC_TCP_CLIENT:
		return InitializeClient(ip, port);

	case NC_UDP:
		return InitializeUDP(ip, port);
	}
	return false;
}

bool network::Initialize(const network* listening)	
{
	if(InitializeAccept(listening))
		return true;
	
	return false;
}

bool network::InitializeListening(string ip, int port)
{
	int retval;

	m_sock = socket(AF_INET, SOCK_STREAM, 0);

	ZeroMemory(&m_addr, sizeof(m_addr));
	m_addr.sin_family = AF_INET;
	m_addr.sin_port = htons(port);
	m_addr.sin_addr.s_addr = htonl(INADDR_ANY);	

	retval = bind(m_sock, (SOCKADDR *)&m_addr, sizeof(m_addr));
	if(retval == SOCKET_ERROR)
		return false;

	retval = listen(m_sock, SOMAXCONN);
	if(retval == SOCKET_ERROR)
		return false;

	m_NetworkType = NC_TCP_LISTENING;
	return true;
}

bool network::InitializeAccept(const network* listening) 
{
	m_addrlen = sizeof(m_addr);
	m_sock = accept(listening->GetSocket(), (SOCKADDR*)&m_addr, &m_addrlen);

	if(m_sock == INVALID_SOCKET)
		return false;

	m_NetworkType = NC_TCP_ACCEPT;	
	return true;
}
bool network::InitializeClient(string ip, int port)
{
	int retval;

	m_sock = socket(AF_INET, SOCK_STREAM, 0);

	m_addr.sin_family = AF_INET;
	m_addr.sin_port = htons(port);
	m_addr.sin_addr.s_addr = inet_addr(ip.c_str());

	m_addrlen = sizeof(m_addr);

	retval = connect(m_sock,(SOCKADDR *)&m_addr, m_addrlen);
	if(retval == SOCKET_ERROR)
		return false;

	m_NetworkType = NC_TCP_CLIENT;	
	return true;
}
bool network::InitializeUDP( string ip, int port)
{
	int retval;

	m_sock = socket(AF_INET, SOCK_DGRAM, 0);

	ZeroMemory(&m_addr, sizeof(m_addr));
	m_addr.sin_family = AF_INET;
	m_addr.sin_port = htons(port);
	m_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	retval = bind(m_sock, (SOCKADDR *)&m_addr,sizeof(m_addr));
	if(retval == SOCKET_ERROR)
		return false;

	m_NetworkType = NC_UDP;
	return true;
}

bool network::Attribute(int level, int optname, const char* optval,int optlen)
{
	cs_auto css(&m_SendCriticalSection);
	cs_auto csr(&m_RecvCriticalSection);

	int retval;
	retval = setsockopt(m_sock, level, optname, optval, optlen);
	if(retval == SOCKET_ERROR)
		return false;
	return true;
}
bool network::Send(const char* buf, int len)
{
	cs_auto cs(&m_SendCriticalSection);

	assert(m_NetworkType != NC_TCP_LISTENING);
	assert(m_NetworkType != NC_NONE);

	int retval;
	retval = send(m_sock,buf,len,0);
	if(retval == SOCKET_ERROR)
		return false;

	return true;
}

bool network::SendUDP(const char* buf, int len, ip recvIP, port port) {
	cs_auto cs(&m_SendCriticalSection);

	assert(m_NetworkType == NC_UDP);

	static SOCKADDR_IN serveraddr;
	ZeroMemory(&serveraddr, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr = recvIP;
	serveraddr.sin_port = port;

	int retval;
	retval = sendto(m_sock, buf, len, 0, (SOCKADDR*)&serveraddr, sizeof(serveraddr));
	
	if(retval == SOCKET_ERROR)
		return false;
	else 
		return true;	
}

bool network::SendGroup(const char* buf, int len) {
	bool Result = true;
	for(target_group::iterator it = m_MulticastGroup.begin();
		it != m_MulticastGroup.end();
		++it) {
		ip TargetIP;	TargetIP.s_addr = it->first;
		Result = Result && (SendUDP(buf, len, TargetIP, it->second));	
	}

	return Result;
}

bool network::Recv(char* buf, const int len, int* reallen)
{
	cs_auto cs(&m_RecvCriticalSection);

	assert(m_NetworkType != NC_TCP_LISTENING);
	assert(m_NetworkType != NC_NONE);

	int received = 0;
	char* ptr = buf;
	int left = len;

	while(left > 0)
	{
		received = recv(m_sock, ptr, left, NULL);
		
		if(received == SOCKET_ERROR) {
			if(winsock::GetErrorCode() == WSAEWOULDBLOCK)
				continue; // it is slow. so this project don't use it. if use it, remodeling network class. 

			return false;		
		}
		else if(received == 0)
			break;

		left -= received;
		ptr += received;
	}

	if(reallen != NULL)
		*reallen = len - left;	
	
	if((len - left) == len)		
		return true;
	else
		return false;
}

bool network::RecvUDP(char* buf, const int len, int* reallen, ip* recvIP, port* port) {
	cs_auto cs(&m_RecvCriticalSection);

	assert(m_NetworkType == NC_UDP);
	static SOCKADDR_IN clientaddr;
	static int addrlen = sizeof(clientaddr);

	int retval = 0;
	retval = recvfrom(m_sock, buf, len, 0, (SOCKADDR*)&clientaddr, &addrlen);
	if(retval == SOCKET_ERROR) 
		return false;

	if(reallen != NULL)
		*reallen = retval;
	if(recvIP != NULL)
		*recvIP = clientaddr.sin_addr;
	if(port != NULL)
		*port = clientaddr.sin_port;

	return true;
}

SOCKET network::GetSocket(void) const
{
	assert(m_sock != NULL);

	return m_sock;
}
in_addr network::GetLinkIP(void)
{
	m_addrlen = sizeof(m_sock);
	getpeername(m_sock, (SOCKADDR *)&m_addr, &m_addrlen);

	return m_addr.sin_addr;
}
bool network::Close(void)
{
	if(m_sock == NULL)
		return true;

	int retval;
	retval = closesocket(m_sock);
	
	if(retval == SOCKET_ERROR)
		return false;
	
	m_sock = NULL;
	return true;
}

NETWORK_CODE network::GetNetworkType(void) {
	return m_NetworkType;
}

void network::AddMulticastGroup(ip TargetIP, port TargetPort) {
	cs_auto cs(&m_SendCriticalSection);

	assert(m_NetworkType == NC_UDP);

	m_MulticastGroup.push_back(make_pair((int)TargetIP.s_addr, TargetPort));
}

bool network::DeleteMulticastGroup(ip TargetIP, port TargetPort) {
	cs_auto cs(&m_SendCriticalSection);

	assert(m_NetworkType == NC_UDP);

	target_group::iterator FindIT = find(m_MulticastGroup.begin(), m_MulticastGroup.end(), 
							make_pair((int)TargetIP.s_addr, TargetPort));

	if(FindIT == m_MulticastGroup.end())
		return false;

	m_MulticastGroup.erase(FindIT);

	return true;
}

bool network::SetEvent(long Events) {
	cs_auto csr(&m_RecvCriticalSection);
	cs_auto css(&m_SendCriticalSection);

	if(m_Event != NULL) {
		WSACloseEvent(m_Event);
		m_Event = NULL;
	}

	assert(m_sock != NULL);
	assert(m_Event == NULL);
	
	m_Event = WSACreateEvent();
	if(WSAEventSelect(m_sock, m_Event, Events) == SOCKET_ERROR)
		return false;
	else
		return true;	
}

void network::EventProcess(unsigned int msTimeout) {
	cs_auto csr(&m_RecvCriticalSection);
	cs_auto css(&m_SendCriticalSection);
	assert(m_sock != NULL);
	assert(m_Event != NULL);

	int retval;

	m_ResultEventProcess = 0;
	retval = WSAWaitForMultipleEvents(1, &m_Event, FALSE, msTimeout, FALSE);
	if(retval == WSA_WAIT_FAILED)
		return;
	
	WSANETWORKEVENTS WNE;
	retval = WSAEnumNetworkEvents(m_sock, m_Event, &WNE);
	if(retval == SOCKET_ERROR)
		return;
	
	if((WNE.lNetworkEvents & FD_READ) && (WNE.iErrorCode[FD_READ_BIT] == 0))
		m_ResultEventProcess += FD_READ;

	if((WNE.lNetworkEvents & FD_WRITE) && (WNE.iErrorCode[FD_WRITE_BIT] == 0))
		m_ResultEventProcess += FD_WRITE;

	if((WNE.lNetworkEvents & FD_ACCEPT) && (WNE.iErrorCode[FD_ACCEPT_BIT] == 0))
		m_ResultEventProcess += FD_ACCEPT;

	if((WNE.lNetworkEvents & FD_CONNECT) && (WNE.iErrorCode[FD_CONNECT_BIT] == 0))
		m_ResultEventProcess += FD_CONNECT;

	if(WNE.lNetworkEvents & FD_CLOSE)
		m_ResultEventProcess += FD_CLOSE;
}

bool network::CheckEvent(long EventCode) {
	if(m_ResultEventProcess & EventCode)
		return true;
	else
		return false;
}

